﻿/**************************************************************************
创建日期:	2019/7/16 9:21:44     
作	  者:	张存
邮 	  箱:	zhangcunliang@126.com
Copyright (c)  ZhCun.cn

描	述： 登录状态缓存处理
记	录： 2019.8.14 增加Redis的缓存机制
***************************************************************************/
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using ZhCun.Utils;
using ZhCun.Utils.Helpers;
using ZhCun.WebCommon.DBModels;

namespace ZhCun.WebCommon.AL
{
    public static class LoginStateCache
    {
        static LoginStateCache()
        {
            if ("Redis".Equals(AppCommonConfig.AppSettings.LoginStateCacheType, StringComparison.CurrentCultureIgnoreCase))
            {
                CacheObject = new LoginStateRedis();
            }
            else
            {
                CacheObject = new LoginStateMemory();
            }
        }

        private readonly static ILoginStateCache CacheObject;

        /// <summary>
        /// 检查Token是否有效
        /// </summary>
        public static bool CheckToken(string token)
        {
            return CacheObject.CheckToken(token);
        }
        /// <summary>
        /// 获取LoginToken对象
        /// </summary>
        public static TSysLoginToken Get(string token)
        {
            return CacheObject.Get(token);
        }
        /// <summary>
        /// 设置缓存
        /// </summary>
        public static void Set(params TSysLoginToken[] tokens)
        {
            CacheObject.Set(tokens);
        }
        /// <summary>
        /// 移除缓存
        /// </summary>
        public static void Remove(params string[] tokens)
        {
            CacheObject.Remove(tokens);
        }
        /// <summary>
        /// 初始化Token，
        /// memory: 从数据库中异步获取所有未到期的token列表，并放入缓存(异步)        
        /// </summary>
        public static void InitCache()
        {
            CacheObject.InitCache();
        }

        public static void ResetExpire(TSysLoginToken tokenObj)
        {
            CacheObject.ResetExpire(tokenObj);
        }
    }

    public interface ILoginStateCache
    {
        /// <summary>
        /// 初始化缓存
        /// </summary>
        void InitCache();
        /// <summary>
        /// 检查token是否存在
        /// </summary>
        bool CheckToken(string token);
        /// <summary>
        /// 获取指定token
        /// </summary>
        TSysLoginToken Get(string token);
        /// <summary>
        /// 设置token
        /// </summary>
        /// <param name="users"></param>
        void Set(params TSysLoginToken[] users);
        /// <summary>
        /// 移除token
        /// </summary>
        void Remove(params string[] tokens);
        /// <summary>
        /// 刷新token过期时间
        /// </summary>
        bool ResetExpire(TSysLoginToken tokenObj);
    }

    public class LoginStateMemory : ILoginStateCache
    {
        /// <summary>
        /// 从数据库中异步获取所有未到期的token列表，并放入缓存(异步)
        /// </summary>
        public void InitCache()
        {
            Task.Run(() =>
            {
                ALUserBase al = new ALUserBase();
                var userList = al.GetValidToken();
                Set(userList.ToArray());
                LogHelper.LogObj.Info($"Token载入缓存: {userList.Count}");
                return userList.Count;
            });
        }
        /// <summary>
        /// 检查Token是否有效
        /// </summary>
        public bool CheckToken(string token)
        {
            if (string.IsNullOrEmpty(token))
            {
                return false;
            }
            //token = token.ToLower();
            return MemoryCacheHelper.Exists(token);
        }
        /// <summary>
        /// 获取LoginToken对象
        /// </summary>
        public TSysLoginToken Get(string token)
        {
            if (string.IsNullOrEmpty(token))
            {
                return null;
            }
            var r = MemoryCacheHelper.Get<TSysLoginToken>(token);
            return r;
        }
        /// <summary>
        /// 设置缓存
        /// </summary>
        public void Set(params TSysLoginToken[] users)
        {
            if (users != null)
            {
                foreach (TSysLoginToken user in users)
                {
                    TimeSpan ts = user.LoginExpiresTime - DateTime.Now;
                    MemoryCacheHelper.Set(user.LoginToken, user, ts);
                }
            }
        }
        /// <summary>
        /// 移除缓存
        /// </summary>
        public void Remove(params string[] tokens)
        {
            if (tokens != null)
            {
                foreach (string token in tokens)
                {
                    MemoryCacheHelper.Remove(token);
                }
            }
        }
        /// <summary>
        /// 刷新token过期时间
        /// </summary>
        public bool ResetExpire(TSysLoginToken tokenObj)
        {
            // ajax请求与页面请求都增加缓存的过期时间
            DateTime newExpiresTime = DateTime.Now.AddMinutes(tokenObj.LoginExpiresMinute);
            if (tokenObj.LoginExpiresTime < DateTime.Now.AddMinutes(tokenObj.LoginExpiresMinute / 3))
            {
                //过期时间小于一半时间的时候进行更新数据库的token过期时间
                // 过期时间（11:30） < 当前时间（11:00） + 过期分钟数（60） / 3
                new ALUserBase().RefreshTokenAsync(tokenObj.LoginToken, newExpiresTime);
            }
            tokenObj.LoginExpiresTime = newExpiresTime;

            TimeSpan ts = tokenObj.LoginExpiresTime - DateTime.Now;
            MemoryCacheHelper.Set(tokenObj.LoginToken, tokenObj, ts);
            return true;
        }
    }

    public class LoginStateRedis : ILoginStateCache
    {
        /// <summary>
        /// redis 对象
        /// </summary>
        private static ConnectionMultiplexer _Redis;
        /// <summary>
        /// redis 数据库
        /// </summary>
        private static IDatabase _RedisDB;
        /// <summary>
        /// 从数据库中异步获取所有未到期的token列表，并放入缓存(异步)
        /// </summary>
        public void InitCache()
        {
            _Redis = ConnectionMultiplexer.Connect(AppCommonConfig.AppSettings.LoginStateRedisConfig);
            _RedisDB = _Redis.GetDatabase();
        }
        /// <summary>
        /// 检查Token是否有效
        /// </summary>
        public bool CheckToken(string token)
        {
            if (string.IsNullOrEmpty(token))
            {
                return false;
            }
            if (MemoryCacheHelper.Exists(token))
            {
                return true;
            }
            return _RedisDB.KeyExists(token);
        }
        /// <summary>
        /// 获取LoginToken对象
        /// </summary>
        public TSysLoginToken Get(string token)
        {
            if (string.IsNullOrEmpty(token))
            {
                return null;
            }
            var tokenObj = MemoryCacheHelper.Get<TSysLoginToken>(token);
            if (tokenObj == null)
            {
                var fileds = _RedisDB.HashGetAll(token);
                if (fileds != null && fileds.Length == 12)
                {
                    tokenObj = new TSysLoginToken();
                    tokenObj.LoginToken = fileds[0].Value;
                    tokenObj.RefreshToken = fileds[1].Value;
                    tokenObj.LoginClientSN = fileds[2].Value;
                    tokenObj.LoginUserId = fileds[3].Value;
                    tokenObj.LoginName = fileds[4].Value;
                    tokenObj.RoleId = fileds[5].Value;
                    tokenObj.RoleName = fileds[6].Value;
                    tokenObj.LoginExpiresMinute = (int)fileds[7].Value;
                    tokenObj.LoginExpiresTime = DateTime.Parse(fileds[8].Value);
                    tokenObj.LoginHost = fileds[9].Value;
                    tokenObj.LoginMode = fileds[10].Value;
                    tokenObj.LoginState = ((int)fileds[11].Value) == 1 ? true : false;
                    TimeSpan ts = tokenObj.LoginExpiresTime - DateTime.Now;
                    MemoryCacheHelper.Set(tokenObj.LoginToken, tokenObj, ts);
                }
            }
            return tokenObj;
        }
        /// <summary>
        /// 设置缓存
        /// </summary>
        public void Set(params TSysLoginToken[] users)
        {
            if (users != null)
            {
                foreach (TSysLoginToken user in users)
                {
                    TimeSpan ts = user.LoginExpiresTime - DateTime.Now;
                    HashEntry[] fileds = new HashEntry[12];
                    fileds[0] = new HashEntry(nameof(user.LoginToken), user.LoginToken);
                    fileds[1] = new HashEntry(nameof(user.RefreshToken), user.RefreshToken);
                    fileds[2] = new HashEntry(nameof(user.LoginClientSN), user.LoginClientSN);
                    fileds[3] = new HashEntry(nameof(user.LoginUserId), user.LoginUserId);
                    fileds[4] = new HashEntry(nameof(user.LoginName), user.LoginName);
                    fileds[5] = new HashEntry(nameof(user.RoleId), user.RoleId);
                    fileds[6] = new HashEntry(nameof(user.RoleName), user.RoleName ?? string.Empty);
                    fileds[7] = new HashEntry(nameof(user.LoginExpiresMinute), user.LoginExpiresMinute);
                    fileds[8] = new HashEntry(nameof(user.LoginExpiresTime), user.LoginExpiresTime.ToString("yyyy-MM-dd HH:mm:ss"));
                    fileds[9] = new HashEntry(nameof(user.LoginHost), user.LoginHost ?? string.Empty);                    
                    fileds[10] = new HashEntry(nameof(user.LoginMode), user.LoginMode);
                    fileds[11] = new HashEntry(nameof(user.LoginState), user.LoginState);
                    //fileds[12] = new HashEntry(nameof(user.MobileNumber), user.MobileNumber);
                    _RedisDB.HashSet(user.LoginToken, fileds);
                    _RedisDB.KeyExpire(user.LoginToken, ts);
                    MemoryCacheHelper.Set(user.LoginToken, user, ts);
                }
            }
        }
        /// <summary>
        /// 重置缓存过期时间
        /// </summary>
        public bool ResetExpire(TSysLoginToken tokenObj)
        {
            tokenObj.LoginExpiresTime = DateTime.Now.AddMinutes(tokenObj.LoginExpiresMinute);
            TimeSpan ts = tokenObj.LoginExpiresTime - DateTime.Now;
            MemoryCacheHelper.Set(tokenObj.LoginToken, tokenObj, ts);
            _RedisDB.KeyExpire(tokenObj.LoginToken, tokenObj.LoginExpiresTime);
            return true;
        }
        /// <summary>
        /// 移除缓存
        /// </summary>
        public void Remove(params string[] tokens)
        {
            if (tokens != null)
            {
                foreach (string token in tokens)
                {
                    _RedisDB.KeyDelete(token);
                    MemoryCacheHelper.Remove(token);
                }
            }
        }
    }
}